/**
 * Agent & 支付相关服务
 */
import axios from "axios";
import crypto from "crypto";
import { URL } from "url";
import fs from "fs";

import { updateOrderPaymentByOutTradeNo } from "@/lib/courtBookingService";


//获取微信支付签名
const merchantId = process.env.MERCHANT_ID;
const openChatAppid = process.env.WECHAT_APP_ID;
const certificateSerialNo = process.env.WECHAT_PAY_CERTIFICATE_SERIAL_NO;
const v3Key = process.env.WECHAT_PAY_V3_KEY;
const WECHAT_PAY_PRIVATE_KEY = fs.readFileSync(process.env.WECHAT_PAY_PRIVATE_KEY_PATH); // 私钥内容应该是一个PEM格式的字符串
const WECHAT_PAY_CERTIFICATE_PATH = fs.readFileSync(process.env.WECHAT_PAY_CERTIFICATE_PATH); // 平台公钥内容应该是一个PEM格式的字符串
const certificatePlatformSerialNo = process.env.WECHAT_PAY_CERTIFICATE_PLATFORM_SERIAL_NO;
function generateNonceStr(length = 16) {
  const chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}

function getToken(method, url, body) {
  const nonceStr = generateNonceStr();
  const timestamp = Math.floor(Date.now() / 1000);
  const message = buildMessage(method, url, timestamp, nonceStr, body);
  const signature = sign(message);

  return (
    `WECHATPAY2-SHA256-RSA2048 ` +
    `mchid="${merchantId}",` +
    `nonce_str="${nonceStr}",` +
    `timestamp="${timestamp}",` +
    `serial_no="${certificateSerialNo}",` +
    `signature="${signature}"`
  );
}

//签名
function sign(message) {
  const sign = crypto.createSign("RSA-SHA256");
  sign.update(message);
  sign.end();

  return sign.sign(WECHAT_PAY_PRIVATE_KEY, "base64");
}

//验证签名
function verify(message, signature) {
  const verify = crypto.createVerify("RSA-SHA256");
  verify.update(message);
  verify.end();

  return verify.verify(WECHAT_PAY_CERTIFICATE_PATH, signature, "base64");
}

function buildMessage(method, url, timestamp, nonceStr, body) {
  const parsedUrl = new URL(url);
  let canonicalUrl = parsedUrl.pathname;
  if (parsedUrl.search) {
    canonicalUrl += parsedUrl.search;
  }

  return (
    `${method}\n` +
    `${canonicalUrl}\n` +
    `${timestamp}\n` +
    `${nonceStr}\n` +
    `${body}\n`
  );
}

/**
 * https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html
 *
 *  return {
  "prepay_id" : "wx201410272009395522657a690389285100"
}
 * 生成预支付订单
 * @param {*} openid 用户openid
 * @param {*} description 订单描述
 * @param {*} totalFee 单位为分
 * @param {*} tradeNo 订单号,32 位
 * @returns {Promise<any>}
 */
export async function jsapiPreOrder({
  openid,
  description,
  totalFee,
  tradeNo,
}) {
  'use server'
  const url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
  const method = "POST";
  const body = JSON.stringify({
    appid: openChatAppid,
    mchid: merchantId,
    description: description,
    out_trade_no: tradeNo,
    attach: `some attach info here`,
    notify_url: "https://whentimes.com/ty/api/wepay",//支付回调通知地址
    amount: {
      total: totalFee,
      currency: "CNY",
    },
    payer: {
      openid: openid,
    },
  });
  const token = getToken(method, url, body);
  console.log("token", token);

  try{
    const response = await axios.post(url, body, {
      headers: {
        Authorization: token,
        "Content-Type": "application/json",
      },
    });
    console.log("response", response);
    const order = await queryOrderByOutTradeNo(tradeNo);
    console.log("order", order);

  if (order.trade_state === "NOTPAY") {
    await updateOrderPaymentByOutTradeNo(tradeNo, {
      is_paid: false,
      status: "active",
      memo: response.data.prepay_id, //prepay_id is
    });
    console.log("update the order memo with prepay_id", response.data.prepay_id);
  }
  return response.data;
  }catch(error){
    console.log("error", error);
  }
}

/**
 * 通过商户号查询订单
 * 交易状态：
 * 【交易状态】 交易状态，枚举值：
* SUCCESS：支付成功
* REFUND：转入退款
* NOTPAY：未支付
* CLOSED：已关闭
* REVOKED：已撤销（仅付款码支付会返回）
* USERPAYING：用户支付中（仅付款码支付会返回）
* PAYERROR：支付失败（仅付款码支付会返回）

 * https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/query-by-out-trade-no.html

curl -X GET \
  https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/1217752501201407033233368018?mchid=1230000109 \
  -H "Authorization: WECHATPAY2-SHA256-RSA2048 mchid=\"1900000001\",..." \
  -H "Accept: application/json"

 * response example:
   {
2  "appid" : "wxd678efh567hg6787",
3  "mchid" : "1230000109",
4  "out_trade_no" : "1217752501201407033233368018",
5  "transaction_id" : "1217752501201407033233368018",
6  "trade_type" : "MICROPAY",
7  "trade_state" : "SUCCESS",
8  "trade_state_desc" : "支付失败，请重新下单支付",
9  "bank_type" : "CMC",
10  "attach" : "自定义数据",
11  "success_time" : "2018-06-08T10:34:56+08:00",
12  "payer" : {
13    "openid" : "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o\t"
14  },
15  "amount" : {
16    "total" : 100,
17    "payer_total" : 100,
18    "currency" : "CNY",
19    "payer_currency" : "CNY"
20  },
21  "scene_info" : {
22    "device_id" : "013467007045764"
23  },
24  "promotion_detail" : [
25    {
26      "coupon_id" : "109519",
27      "name" : "单品惠-6",
28      "scope" : "GLOBAL",
29      "type" : "CASH",
30      "amount" : 100,
31      "stock_id" : "931386",
32      "wechatpay_contribute" : 0,
33      "merchant_contribute" : 0,
34      "other_contribute" : 0,
35      "currency" : "CNY",
36      "goods_detail" : [
37        {
38          "goods_id" : "M1006",
39          "quantity" : 1,
40          "unit_price" : 100,
41          "discount_amount" : 1,
42          "goods_remark" : "商品备注信息"
43        }
44      ]
45    }
46  ]
47}
 */

export async function queryOrderByOutTradeNo(outTradeNo) {
  const url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=${merchantId}`;
  const method = "GET";
  const body = "";
  const token = getToken(method, url, body);
  console.log("token", token);

  const response = await axios.get(url, {
    headers: {
      Authorization: token,
      "Content-Type": "application/json",
    },
  });

  return response.data;
}

/**
 * 生成jsapi支付签名
 * 签名串一共有四行，每一行为一个参数。行尾以\n（换行符，ASCII编码值为0x0A）结束，包括最后一行。
如果参数本身以\n结束，也需要附加一个\n

参数格式：

应用ID
时间戳
随机字符串
订单详情扩展字符串

使用商户私钥对_待签名串_进行SHA256 with RSA签名，并对签名结果进行_Base64编码_得到签名值。
 */

export function generateJsApiPaySign({ timestamp, nonceStr, packageName }) {
  const stringA = `${openChatAppid}\n${timestamp}\n${nonceStr}\n${packageName}\n`;
  const signature = sign(stringA);
  return signature;
}

/**
 * 验证微信回调签名
 * https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html
 *
 * 首先，您从应答或通知回调中获取以下信息：

HTTP 头 Wechatpay-Timestamp 中的应答时间戳
HTTP 头 Wechatpay-Nonce 中的应答随机串
应答报文主体（Response Body），请使用原始报文主体执行验签。如果您使用了某个框架，要确保它不会篡改报文主体。对报文主体的任何篡改都会导致验证失败。
然后，请按照以下规则构造应答的验签名串。签名串共有三行，行尾以 \n 结束，包括最后一行。\n 为换行符（ASCII 编码值为 0x0A）。若应答报文主体为空（如 HTTP 状态码为 204 No Content），最后一行仅为一个 \n 换行符。

应答时间戳\n
应答随机串\n
应答报文主体\n
以某次下载微信支付平台证书 API 的应答 HTTP 报文（省略了 ciphertext 的具体内容）为例：

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 02 Apr 2019 12:59:40 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2204
Connection: keep-alive
Keep-Alive: timeout=8
Content-Language: zh-CN
Request-ID: e2762b10-b6b9-5108-a42c-16fe2422fc8a
Wechatpay-Nonce: c5ac7061fccab6bf3e254dcf98995b8c
Wechatpay-Signature: CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==
Wechatpay-Timestamp: 1554209980
Wechatpay-Serial: 5157F09EFDC096DE15EBE81A47057A7232F1B8E1
Cache-Control: no-cache, must-revalidate
{"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"4de73afd28b6","associated_data":"certificate","ciphertext":"..."}}]}
则验签名串为

1554209980\n
c5ac7061fccab6bf3e254dcf98995b8c\n
{"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"4de73afd28b6","associated_data":"certificate","ciphertext":"..."}}]}\n
#
 */
export function decryptResource(resource, apiV3Key) {
  const { ciphertext, associated_data, nonce } = resource;

  const key = Buffer.from(apiV3Key, "utf8");
  const decodedNonce = Buffer.from(nonce, "utf8");
  const decodedCiphertext = Buffer.from(ciphertext, "base64");

  // 分离 authTag 和加密数据
  const authTag = decodedCiphertext.slice(-16);
  const encryptedData = decodedCiphertext.slice(0, -16);

  try {
    const decipher = crypto.createDecipheriv("aes-256-gcm", key, decodedNonce);
    decipher.setAuthTag(authTag);

    if (associated_data) {
      decipher.setAAD(Buffer.from(associated_data, "utf8"));
    }

    let decrypted = decipher.update(encryptedData);
    decrypted = Buffer.concat([decrypted, decipher.final()]);

    const decryptedText = decrypted.toString("utf8");

    return JSON.parse(decryptedText);
  } catch (err) {
    console.error("解密错误详情:", err);
    throw new Error(`解密失败: ${err.message}`);
  }
}

export function verifyWeChatCallbackSign({timestamp, nonce, signature, serial, body}) {

  if (serial !== certificatePlatformSerialNo) {
    console.log("invalid certificate serial number", serial, certificatePlatformSerialNo);
    return { isValid: false, error: "invalid certificate serial number" };
  }

  const stringA = `${timestamp}\n${nonce}\n${JSON.stringify(body)}\n`;
  const isValid = verify(stringA, signature);
  console.log("verify the wechat pay callback sign", isValid);

  if (!isValid) {
    console.log("invalid signature", stringA, signature);
    return { isValid: false, error: "invalid signature" };
  }

  try {
    const decryptedResource = decryptResource(body.resource, v3Key);
    console.log("解密出来的资源", JSON.stringify(decryptedResource, null, 2));
    return { isValid: true, wepayOrder: decryptedResource };
  } catch (error) {
    console.error("解密失败:", error);
    return { isValid: false, error: error.message };
  }
}
